# ------------------------------------------
#※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
# File Name: assertions.sh
# Author: GID5564
# Description:
# Created Time: 06/11/23-20:18:14
#※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
#! /usr/bin/env bash


# 避免重复导入
[[ -n $__XLIB_IMPORTED__ASSERTIONS ]] && return 0
__XLIB_IMPORTED__ASSERTIONS=1


function __assertions_init__() {
    wait
}


# arg-count <actual> <expected> [message]
# 校验入参个数
function check_arg_count() {
    [[ -z "$1" ]] && {
        error "Usage: check_arg_count <actual> <expected> [message]"
        return $INVALID_ARGUMENTS
    }

    local actual=$1
    local expected=$2
    local message=${3:-"Expected $expected arguments, got $actual arguments"}
    ((actual == expected)) || {
        error "$message"
        exit $INVALID_ARGUMENTS
    }
    unset actual
    unset exected
    unset message
}

# regex <string> <regex> [message]
# 校验字符串是否符合指定的正则表达式
function regex_match() {
    [[ -z "$1" ]] && {
        error "Usage: regex_match <string> <regex> [message]"
        return $INVALID_ARGUMENTS
    }
    local str=$1
    local regex=$2
    local message=${3:-"String '$stri' does not match regex '$regex'"}
    [[ $str =~ $regex ]] || {
        error "$message"
        exit $STRING_REGX_NOT_MATCH
    }
    unset str
    unset regex
    unset message
}

# 断言变量是否存在
# defined <var-name-not-$var-name>
# defined -f <var-nar-not-$var-name>
# defined "s": s:变量名称，非 $s
# defined -f: 不存在时强制exit
function assert::defined() {
    local fatal
    local var
    local num_null=0
    [[ "$1" = "-f" ]] && {
        fatal=1
        shift
    }
    for var in "$@"; do
        [[ -z "${!var}" ]] && printf '%s\n' "Variable '$var' not set" >&2 && ((num_null++))
    done

    if ((num_null > 0)); then
        [[ "$fatal" ]] && exit 1
        return $GENERIC_ERROR
    fi
    return 0
}

#
# assert if $1 is a valid URL
#
function valid_url() {
    [[ -z "$1" ]] && {
        error "Usage: valid_url <url> "
        return $INVALID_ARGUMENTS
    }

    (($#)) || return 0
    local url=$1
    curl --fail --head -o /dev/null --silent "$url" || {
        error "Invalid URL - '$url'"
        exit $GENERIC_ERROR
    }
    unset url
}

# 检查cmd是否在PATH中
# check-cmd <command>
function check_cmd() {
    [[ -z "$1" ]] && {
        error "Usage: check_cmd <cmd>"
        return $INVALID_ARGUMENTS
    }
    local cmd="$1"

    if eval type type >/dev/null 2>&1; then
        eval type "$cmd" >/dev/null 2>&1
    elif command >/dev/null 2>&1; then
        command -v "$cmd" >/dev/null 2>&1
    else
        which "$cmd" >/dev/null 2>&1
    fi

    die $? "check_cmd: $cmd failure"
    unset cmd

}

# chec-files <file or file list>
# 检查文件是否存在，有一个不存在则exit
function assert::check_files() {
    [[ -z "$1" ]] && {
        error "Usage: check_files <file| file list>"
        return $INVALID_ARGUMENTS
    }
    local file
    local rc=0
    for file; do
        if [[ ! -f "$file" && ! -d "$file" ]]; then
            error "File '$file' does not exist"
            ((rc++))
        fi
    done

    [[ $rc -gt 0 ]] && exit 1
    unset file
    unset rc
    return 0
}


# 命令输出结果判断
# assert::cmd-equail <cmd> <expected> [stdin]
function cmd_equal() {
    [[ -z "$1" ]] && {
        error "Usage: cmd_equail <cmd> <expected> [stdin]"
        return $INVALID_ARGUMENTS
    }
    local _indent=$'\n\t'
    local expected=$(echo -ne "${2:-}")
    local result="$(eval 2>/dev/null $1 <<<${3:-})" || true

    if [[ "$result" == "$expected" ]]; then
        unset result
        unset _index
        unset expected
        return $GENERIC_NORMAL
    fi

    result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<<"$result")"
    [[ -z "$result" ]] && result="nothing" || result="\"$result\""
    [[ -z "$2" ]] && expected="nothing" || expected="\"$2\""
    error "expected $expected${_indent}got $result" "$1" "${3:-}"
    unset result
    unset _index
    unset expected
    exit $STRING_NOT_EQUAL
}

# unit::assert_not_empty VALUE [MESSAGE]
# Show error message, when `VALUE` is empty.
function not_empty() {
    [[ -z "$1" ]] && {
        error "Usage: not_empt <value> [message]"
        return $INVALID_ARGUMENTS
    }

    local VALUE="${1:-}"
    local MESSAGE="${2:-Value is empty.}"

    [[ -n "${VALUE:-}" ]] || {
        error "ASSERT FAILED" "$MESSAGE"
        exit 1
    }
    unset VALUE
    unset MESSAGE
}

# equal <string1> <string2> [message]
# 校验两个字符串是否相等
function equal() {
    [[ -z "$1" ]] && {
        error "Usage: equail <str1> <str2> [message]"
        return $INVALID_ARGUMENTS
    }

    local ACTUAL="${1:-}"
    local EXPECTED="${2:-}"
    local MESSAGE="${3:-Values are not equal.}"

    [[ "${ACTUAL:-}" == "${EXPECTED:-}" ]] || {
        error "ASSERT FAILED" "$MESSAGE Actual value: \"${ACTUAL:-}\", expected value: \"${EXPECTED:-}\"."
        unset ACTUAL
        unset EXPECTED
        unset MESSAGE
        exit $STRING_NOT_EQUAL
    }
    unset ACTUAL
    unset EXPECTED
    unset MESSAGE
    return 0
}

# notEqual <string1> <string2> [message]
function not_Equal() {
    [[ -z "$1" ]] && {
        error "Usage: not_equail <str1> <str2> [message]"
        return $INVALID_ARGUMENTS
    }

    local ACTUAL_VALUE="${1:-}"
    local UNEXPECTED_VALUE="${2:-}"
    local MESSAGE="${3:-values are equal but must not.}"

    [ "${ACTUAL_VALUE:-}" != "${UNEXPECTED_VALUE:-}" ] || {
        error "ASSERT FAILED" "$MESSAGE Actual value: \"${ACTUAL_VALUE:-}\", unexpected value: \"$UNEXPECTED_VALUE\"."
        unset ACTUAL_VALUE
        unset EXPECTED
        unset MESSAGE
        exit 1
    }
    unset ACTUAL_VALUE
    unset UNEXPECTED_VALUE
    unset MESSAGE
    return 0
}

# assert <message> <command string>
# 校验command是否正常执行
function assert() {
    [[ -z "$1" ]] && {
        error "Usage: assert <message> <command string>"
        return $INVALID_ARGUMENTS
    }

    local MESSAGE="${1:-}"
    shift

    eval "$@" >/dev/null 2>&1 || {
        error "ASSERT FAILED" "${MESSAGE:-}: $@"
        exit 1
    }
    unset MESSAGE
    return 0
}

# array-equals <message> <a1> <a2>
# ex: a1=(111 222); a2=(aaa bbb); array-equals "mmm" a1 a2
function array_equals() {
    [[ -z "$1" ]] && {
        error "Usage: array_equals <message> <a1> <a2>"
        return $INVALID_ARGUMENTS
    }

    (($# == 2 || $# == 3)) || error "assertArrayEquals must 2 or 3 arguments!"

    local failMsg=""
    (($# == 3)) && {
        failMsg=$1
        shift
    }

    local a1PlaceHolder="$1[@]"
    local a2PlaceHolder="$2[@]"
    local a1=("${!a1PlaceHolder}")
    local a2=("${!a2PlaceHolder}")

    [[ ${#a1[@]} -eq ${#a2[@]} ]] || error "array length [${#a1[@]}] != [${#a2[@]}]${failMsg:+: $failMsg}"

    local i
    for ((i = 0; i < ${#a1[@]}; i++)); do
        [ "${a1[$i]}" = "${a2[$i]}" ] || error "fail element $i: [${a1[$i]}] != [${a2[$i]}]${failMsg:+: $failMsg}"
    done
    unset a1PlaceHolder
    unset a2PlaceHolder
    unset a1
    unset a2
    unset failMsg
    unset i
}


__assertions_init__
























